import re
import os
from datetime import timezone, timedelta, datetime
import argparse
import zipfile
import tarfile
from prettytable import PrettyTable
import logging
from bs4 import BeautifulSoup

Logger = logging.getLogger()
Logger.setLevel(level=logging.INFO)
File_log = logging.FileHandler("./isv.log", "a", encoding="utf-8")
File_log.setFormatter(logging.Formatter("[%(funcName)s - %(lineno)d]  %(message)s"))
Logger.addHandler(File_log)

CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))


class IsvTool:
    def __init__(self):
        self.log = Logger

        # 解决方案对应分析方法
        self.method_dict = {
            "bigdata": self.check_bigdata,
            "storage": self.check_storage,
            "database": self.check_database,
            "arm_native": self.check_arm_native,
            "virtual": self.check_virtual,
            "virt": self.check_virt,
            "acceleration_library": self.check_acceleration_library,
            "hpc": self.check_hpc

        }
        # 项目中文名标识
        self.project_msg = {
            "bigdata": "大数据",
            "storage": "分布式存储",
            "database": "数据库",
            "arm_native": "Arm原生",
            "virtual": "虚拟化",
            "virt": "机密计算",
            "acceleration_library": "加速库",
            "hpc": "HPC"
        }

        # 特性是否必选标识
        self.must_feature = {
            "bigdata": ["OmniRuntime Hbase二级索引", "OmniRuntime 参数调优", "OmniRuntime 算子加速"],
            "storage": ["存储加速算法库"],
            "database": ["GCC编译反馈优化", "MySQL无锁优化"],
            "arm_native": ["视频流引擎"],
            "virtual": ["负载动态感知"],
            "virt": ["virtCCA", "iTrustee"],
            "acceleration_library": ["KAE加解密", "鲲鹏基础加速库 系统库", "鲲鹏基础加速库 HMPP", "鲲鹏基础加速库 数学库"],
            "hpc": ["加速库改造"]
        }
        self.must_str = str(list(self.must_feature.values()))
        self.default_html = "./report.html"

    def check_virtcca_cvm(self, log_path):
        create_cvm_status = False
        start_cvm_status = False
        with open(log_path) as f:
            for per_line in f.readlines():
                if re.compile("launchSecurity type=\'cvm\'>").findall(per_line):
                    create_cvm_status = True

                if re.compile("etc").findall(per_line):
                    start_cvm_status = True

        self.log.info(f"create_cvm_status: {create_cvm_status} start_cvm_status: {start_cvm_status}")
        if start_cvm_status and create_cvm_status:
            return True
        else:
            return False

    def check_ccos(self, log_path):
        ccos_status = False
        with open(log_path) as f:
            content = f.read()
            check_res = re.compile(r"check (s\w+)").findall(content)
            if check_res:
                ccos_status = True

            if re.compile(r"TA_UUID").findall(content):
                ccos_status = True
            else:
                ccos_status = False

        return ccos_status

    def check_virt(self, *log_path_list):
        result = {"virtCCA": False, "iTrustee": False}
        for per_log in log_path_list:
            per_log_strip = per_log.replace("./", "")
            if per_log_strip.startswith("virtcca"):
                result["virtCCA"] = self.check_virtcca_cvm(per_log)
            elif per_log_strip.startswith("virtccos"):
                result["iTrustee"] = self.check_ccos(per_log)

        return result

    def check_arm_native(self, log_path):
        kbox, video, instruction = False, False, False
        kbox_key_word = (
            'gralloc.kbox.so', 'audio.primary.kbox.so', 'gps.kbox.so', 'sensors.kbox.so', 'libmedia_omxcore.so',
            'libstagefrighthw', 'vinput', 'hwcomposer.kbox.so')
        with open(log_path) as f:
            content = f.read()
            for per_key_word in kbox_key_word:
                kbox_res = re.compile(per_key_word).findall(content)
                if kbox_res:
                    kbox = True
                    break

            video_res = re.compile("VmiInputFlinger").findall(content)
            if video_res:
                video = True

            instruction_res = re.compile("VmiAgent instruction").findall(content)
            if instruction_res:
                instruction = True

        return {"Kbox基础云手机": kbox, "视频流引擎": video, "指令流引擎": instruction}

    def check_virtual_flow(self, log_path):
        # ovs 卸载检测
        status = False
        with open(log_path) as f:
            content = f.read()
            if not content:
                return status

            if not re.compile("ovs-appctl: command not found").findall(content) and not re.compile(
                    "No flow available").findall(content):
                status = True

        return status

    def check_virtual_dpu(self, log_path):
        net_uninstall, storage_uninstall, openstack_uninstall = False, False, False
        with open(log_path) as f:
            content = f.read()
            if not content:
                return False

            if not re.compile("dpak_ovs_ctl: command not found").findall(content) and not re.compile(
                    "No flow available").findall(content):
                net_uninstall = True
            content_new = content.replace("\n", "")
            storage_res = re.compile('\"session\": \[(.*)\]\s*\}').findall(content_new)
            self.log.info(f"storage_res: %s" % storage_res)
            if storage_res:
                if storage_res[0].strip() == "":
                    storage_uninstall = False
                else:
                    storage_uninstall = True

            server_name = re.compile(r"server_name: (.*)").findall(content)
            self.log.info(f"server_name: %s" % server_name)
            if not server_name or server_name[0] == "":
                openstack_uninstall = False
            elif re.compile(r"%s\s+|\s+ACTIVE" % server_name[0]).findall(content):
                openstack_uninstall = True

        if net_uninstall and storage_uninstall and openstack_uninstall:
            return True
        else:
            return False

    def check_virtual_load_awareness(self, log_path):
        # 负载动态感知检测
        running_status = False
        version_status = False
        with open(log_path) as f:
            content = f.read()
            if re.compile("active \(running\)").findall(content):
                running_status = True
            if re.compile("waasctl:\s\d+\S\d+\S\d+").findall(content) and \
                    re.compile("WaaS Agent:\s\d+\S\d+\S\d+").findall(content):
                version_status = True

        if running_status and version_status:
            return True
        else:
            return False

    def check_virtual(self, *log_path_list):
        result = {"负载动态感知": False, "OVS流表网卡加速": False, "虚拟化DPU卸载": False}
        for per_log in log_path_list:
            per_log_strip = per_log.replace("./", "")
            if per_log_strip.startswith("virtual_sense"):
                result.setdefault("负载动态感知", "")
                result["负载动态感知"] = self.check_virtual_load_awareness(per_log)
            elif per_log_strip.startswith("virtual_dpu_flow"):
                result["OVS流表网卡加速"] = self.check_virtual_flow(per_log)
            elif per_log_strip.startswith("virtual_dpu"):
                result["虚拟化DPU卸载"] = self.check_virtual_dpu(per_log)

        return result

    def check_database(self, log_path):
        thread_pool, kovae, gcc_compile, no_lock, crc32 = False, False, False, False, False
        with open(log_path) as f:
            content = f.read()
            if re.compile("THREAD_POOL_GROUPS").findall(content) and re.compile(
                    "thread_pool: thread_pool.so").findall(
                content):
                thread_pool = True

            if re.compile("kovae_path: ha_kovae.so").findall(content) and re.compile("KOVAE_MEMORY_ACTIVE").findall(
                    content):
                kovae = True

            if re.compile("bolt").findall(content):
                gcc_compile = True

            no_lock_list = re.compile("no_lock: (\d+)").findall(content)
            if not no_lock_list:
                no_lock = False
            elif int(no_lock_list[0]) > 0:
                no_lock = True

            if re.compile("crc32cb").findall(content):
                crc32 = True

        return {"MySQL支持线程池功能": thread_pool, "MySQL向量化分析引擎": kovae, "GCC编译反馈优化": gcc_compile, "MySQL无锁优化": no_lock,
                "CRC32指令优化": crc32}

    def check_acceleration_library(self, log_path):
        # 加速库
        kae, system_lib, hmpp_lib, math_lib = False, False, False, False
        with open(log_path) as f:
            content = f.read()
            if not re.compile("invalid engine").findall(content) and re.compile("engine \"kae\" set").findall(content):
                kae = True

            for per_system_flag in ["libavx2ki", "libavx2neon", "libkqmalloc", "libhtl"]:
                if re.compile(f"{per_system_flag}.so").findall(content):
                    system_lib = True
                    self.log.info(f"check system_lib: {per_system_flag}.so result: {system_lib}")
                    break
                self.log.info(f"check system_lib: {per_system_flag}.so result: {system_lib}")

            for per_hmpp_flag in ["libHMPP_signal", "libHMPP_image", "libHMPP_audio"]:
                if re.compile(f"{per_hmpp_flag}.so").findall(content):
                    hmpp_lib = True
                    self.log.info(f"check hmpp_lib: {per_hmpp_flag}.so result: {hmpp_lib}")
                    break
                self.log.info(f"check hmpp_lib: {per_hmpp_flag}.so result: {hmpp_lib}")

            for per_math_flag in ["libkblas", "libkvml", "libkspblas", "libkfftf", "libkfft", "libkm", "libkm_19",
                                  "libksvml", "libkvsl", "libksolver", "libklapack_full", "libkscasolver", "libkes"]:
                if re.compile(f"{per_math_flag}.so").findall(content):
                    math_lib = True
                    self.log.info(f"check math_lib: {per_math_flag}.so result: {math_lib}")
                    break
                self.log.info(f"check math_lib: {per_math_flag}.so result: {math_lib}")

        return {"KAE加解密": kae, "鲲鹏基础加速库 系统库": system_lib, "鲲鹏基础加速库 HMPP": hmpp_lib, "鲲鹏基础加速库 数学库": math_lib}

    def bigdata_kal(self, log_path):
        # 根据 bigdata_kal 前缀输入; 针对单个算法日志解析是否生效； 外部收集具体算法信息
        kal_status = False
        split_log_path = log_path.split("_")
        alg_name, datatset_name = split_log_path[-2], split_log_path[-1]
        with open(log_path) as f:
            content = f.read()
            if re.compile("KunpengAlgorithmLibrary").findall(content):
                kal_status = True

        self.log.info(f"Algorithm: {alg_name} dataset {datatset_name} check_result：{kal_status}")
        return kal_status

    def bigdata_operator(self, log_path):
        operator_status = False
        with open(log_path) as f:
            content = f.read()
            if re.compile("Using BoostKit Spark Native Sql Engine Extension to Speed Up Your Queries").findall(content):
                operator_status = True

        return operator_status

    def bigdata_hbase(self, log_path):
        hbase_status = False
        with open(log_path) as f:
            content = f.read()
            if re.compile("Best index").findall(content):
                hbase_status = True

        return hbase_status

    def bigdata_tune_up(self, log_path):
        tuneup_status = False
        with open(log_path) as f:
            content = f.read()
            if re.compile("omniadvisor.log").findall(content) and not re.compile("omniadvisor.log not exist").findall(
                    content):
                if re.compile("best_config").findall(content) and re.compile("history_config").findall(content):
                    tuneup_status = True

        return tuneup_status

    def check_bigdata(self, *log_path_list):
        result = {"机器学习和图分析算法加速库": list(), "OmniRuntime Hbase二级索引": False, "OmniRuntime 参数调优": False,
                  "OmniRuntime 算子加速": False}
        for per_log in log_path_list:
            per_log_strip = per_log.replace("./", "")
            if per_log_strip.startswith("bigdata_kal"):
                # result.setdefault("机器学习和图分析算法加速库", list())
                result["机器学习和图分析算法加速库"].append(self.bigdata_kal(per_log))
            elif per_log_strip.startswith("bigdata_hbase"):
                result["OmniRuntime Hbase二级索引"] = self.bigdata_hbase(per_log)
            elif per_log_strip.startswith("bigdata_tune_up"):
                result["OmniRuntime 参数调优"] = self.bigdata_tune_up(per_log)
            elif per_log_strip.startswith("bigdata_operator"):
                result["OmniRuntime 算子加速"] = self.bigdata_operator(per_log)

        bigdata_kal_content = result.get("机器学习和图分析算法加速库")
        self.log.info("bigdata_kal: {}".format(bigdata_kal_content))

        if False in bigdata_kal_content or len(bigdata_kal_content) < 4:
            result["机器学习和图分析算法加速库"] = False
        else:
            result["机器学习和图分析算法加速库"] = True

        return result

    def check_storage(self, log_path):
        # 存储分析
        ec_turbo, cache, ksal = False, False, False
        with open(log_path) as f:
            content = f.read()
            if re.compile(r"libkps_ec.so").findall(content) and re.compile(r"libkps_bluestore.so").findall(content):
                ec_turbo = True
            if re.compile(r"traffic_policy_start").findall(content):
                cache = True
            if re.compile(r"libksal.so").findall(content) and re.compile(r"libec_ksal.so").findall(content):
                ksal = True

        result = {"存储Ceph加速 EC Turbo": ec_turbo, "存储Ceph加速 智能写Cache": cache, "存储加速算法库": ksal}
        self.log.info(f"storage analysis result: {result}")
        return result

    def check_hpc_acc_lib(self, log_path):
        acc_lib = False
        num = 0
        with open(log_path) as f:
            content = f.read()
            for key_word in ["hyperio", "libkm_l", "hmpi", "tydss"]:
                if re.compile(f"{key_word}").findall(content):
                    num += 1

        if num >= 3:
            acc_lib = True

        return acc_lib

    def check_hpc_sme_lib(self, log_path):
        sme_status = False
        with open(log_path) as f:
            content = f.read()
            with open("./SME_ISTRUCTION.txt", encoding="utf-8") as hpc_fuc:
                for per_line in hpc_fuc.readlines():
                    if re.compile(per_line).findall(content):
                        sme_status = True

        return sme_status

    def check_hpc(self, *log_path_list):
        result = {"加速库改造": False, "SME改造": False}
        for per_log in log_path_list:
            per_log_strip = per_log.replace("./", "")
            if per_log_strip.startswith("hpc_acceleration_library"):
                result["加速库改造"] = self.check_hpc_acc_lib(per_log)
            elif per_log_strip.startswith("hpc_SME_library"):
                result["SME改造"] = self.check_hpc_sme_lib(per_log)

        return result

    def induct_log(self, *log_list):
        # 归纳不同解决方案的日志
        induct_result = dict()
        for per_log in log_list:
            if per_log.startswith("./bigdata"):
                induct_result.setdefault("bigdata", list())
                induct_result["bigdata"].append(per_log)
            elif per_log.startswith("./storage"):
                induct_result.setdefault("storage", list())
                induct_result["storage"].append(per_log)
            elif per_log.startswith("./database"):
                induct_result.setdefault("database", list())
                induct_result["database"].append(per_log)
            elif per_log.startswith("./arm_native"):
                induct_result.setdefault("arm_native", list())
                induct_result["arm_native"].append(per_log)
            elif per_log.startswith("./acceleration_library"):
                induct_result.setdefault("acceleration_library", list())
                induct_result["acceleration_library"].append(per_log)
            elif per_log.startswith("./virtual"):
                induct_result.setdefault("virtual", list())
                induct_result["virtual"].append(per_log)
            elif per_log.startswith("./virt"):
                induct_result.setdefault("virt", list())
                induct_result["virt"].append(per_log)
            elif per_log.startswith("./hpc"):
                induct_result.setdefault("hpc", list())
                induct_result["hpc"].append(per_log)
            elif per_log.startswith("./os_msg"):
                induct_result.setdefault("os_msg", None)
                induct_result["os_msg"] = per_log

        self.log.info(f"induct_dict: {induct_result}")
        return induct_result

    def analysis(self, **log_path_dict: dict):
        result = dict()
        if log_path_dict.get("os_msg"):
            log_path_dict.pop("os_msg")

        for per_project, feature_log in log_path_dict.items():
            method = self.method_dict.get(per_project)
            if method:
                if per_project in ["bigdata", "virtual", "virt", "hpc"]:
                    result.setdefault(per_project, method(*feature_log))
                else:
                    result.setdefault(per_project, method(feature_log[-1]))
            else:
                self.log.info(f"{per_project} nocorrect")

        self.log.info(result)
        return result

    @staticmethod
    def decompress_zip(compressed_report_package):
        with zipfile.ZipFile(compressed_report_package) as zip:
            zip.extractall("./")
            file_names = zip.namelist()
            return file_names

    @staticmethod
    def decompress_gz_bz(compressed_report_package):
        with tarfile.open(compressed_report_package, "r") as tar:
            tar.extractall(path="./")
            file_names = tar.getnames()
            return file_names

    def generator_html(self, new_html_path, feature_num, config_dict, feature_msg):
        with open(self.default_html, encoding="UTF-8") as f:
            soup = BeautifulSoup(f.read(), "html.parser")
            aa = soup.find_all("script")
            temp = aa[0].string
            insert_str = """    
            var infoList = {};

            // 已使能特性
            var num = {};   

            // 验收详细结果
            var tableList = {}; 
            {}""".format(
                str(config_dict),
                str(feature_num),
                str(feature_msg),
                temp
            )
        aa[0].string = insert_str
        with open(new_html_path, "w", encoding="UTF-8") as f:
            f.write(str(soup))

    def acquire_os_msg(self, log_path):

        if not log_path:
            self.log.error("系统信息未获取到")
            insert_list = [
                {"label": "报告生成时间",
                 "value": datetime.now(tz=timezone(timedelta(hours=8))).strftime("%Y/%m/%d %H:%M:%S")},
                {"label": "操作系统版本", "value": "未获取"},
                {"label": "内核版本", "value": "未获取"},
                {"label": "鲲鹏服务器的机型", "value": "未获取"},
                {"label": "工具路径", "value": CURRENT_DIR},
                {"label": "工具版本", "value": "1.0"},
            ]
            return insert_list

        os_dict = {
            "os": "None",
            "version": "None",
            "kernel": "None",
            "product_name": "None",
        }
        with open(log_path, encoding="utf-8") as f:
            for per_line in f.readlines():
                per_line = per_line.strip()
                if per_line.startswith("os:"):
                    os_re = re.compile(r"os: (.*)").findall(per_line.strip())
                    os_dict["os"] = os_re[0].strip("\"") if os_re else "None"
                elif per_line.startswith("version:"):
                    version_re = re.compile(r"Version:(.*)").findall(per_line)
                    os_dict["version"] = version_re[0] if version_re else "None"
                elif per_line.startswith("kernel:"):
                    kernel = re.compile(r"kernel: (.*)").findall(per_line)
                    os_dict["kernel"] = kernel[0] if kernel else "None"
                elif per_line.startswith("Product Name:"):
                    product_name_re = re.compile(r"Product Name:(.*)").findall(per_line)
                    os_dict["product_name"] = product_name_re[0] if product_name_re else "None"
                elif per_line.startswith("customer_information:"):
                    customer_information_re = re.compile(r"customer_information:(.*)").findall(per_line)
                    os_dict["customer_information"] = customer_information_re[0] if customer_information_re else "None"

        self.log.info(f"os_dict: {os_dict}")
        insert_list = [
            {"label": "报告生成时间", "value": datetime.now(tz=timezone(timedelta(hours=8))).strftime("%Y/%m/%d %H:%M:%S")},
            {"label": "操作系统版本", "value": os_dict.get("os", "未获取")},
            {"label": "内核版本", "value": os_dict.get("kernel", "未获取")},
            {"label": "鲲鹏服务器的机型", "value": os_dict.get("product_name", "未获取")},
            {"label": "信息收集压缩包名称", "value": os_dict.get("customer_information", "客户未填写")},
            {"label": "工具路径", "value": CURRENT_DIR},
            {"label": "工具版本", "value": "1.0"},
        ]
        self.log.info(f"insert_list: \n{insert_list}")
        return insert_list

    def convert_analysis_res(self, **analysis_result):
        # 转换分析结果
        index = 1
        effect_num = 0
        result_list = list()
        self.log.info(f"must_str: {self.must_str}")
        success_str, fail_str, enable_str = "", "", ""
        for project, features in analysis_result.items():
            # 筛选通过的特性
            temp_key = [key for key, value in features.items() if value]
            # 必选特性情况
            project_must_feature = self.must_feature.get(project)
            # 和必选特性交集
            temp_set = set(temp_key) & set(project_must_feature)
            if False not in features.values():
                effect_num += 1
                success_str += f"{self.project_msg.get(project)} "
            else:
                # 根据必选特性判断是否使能对应解决方案
                enable_list = list()
                for per_feature in project_must_feature:
                    if per_feature not in list(temp_set):
                        fail_str += f"{self.project_msg.get(project)} "
                        break
                    else:
                        enable_list.append(True)

                self.log.info(f"{project} 必选特性生效情况： {temp_set} 所有特性情况 {features}")
                if enable_list and False not in enable_list and len(enable_list) == len(project_must_feature):
                    effect_num += 1
                    success_str += f"{self.project_msg.get(project)} "

            for per_feature, status in features.items():
                status = "通过" if status else "失败"
                must_status = "必选" if per_feature in self.must_str else "非必选"
                result_list.append([index, self.project_msg.get(project), per_feature, must_status, status])
                index += 1

        # 转化使能评估结果适用于html 数据填充
        if success_str == "" and fail_str != "":
            enable_list = [{"label": "使能评估失败", "value": "{} 【失败】".format(fail_str)}]
        elif success_str != "" and fail_str == "":
            enable_list = [{"label": "使能评估成功", "value": "{} 【通过】".format(success_str)}]
        elif success_str == "" and fail_str == "":
            enable_list = [{"label": "使能评估结果", "value": "未使能任何解决方案"}]
        else:
            enable_list = [{"label": "使能评估成功", "value": "{} 【通过】".format(success_str)},
                           {"label": "使能评估失败", "value": "{} 【失败】".format(fail_str)},
                           ]

        return result_list, effect_num, enable_list


if __name__ == "__main__":
    # try:
    parser = argparse.ArgumentParser()
    parser.add_argument('-tool', help='DevKit or BoostKit')
    parser.add_argument('-package', help='Compressed package')
    args = parser.parse_args()
    if args.tool == "DevKit":
        pass
        # acceptance_tool = AcceptanceTool()
        # acceptance_tool.devkit_acceptance_report(args.package)
    elif args.tool == "BoostKit":
        boostkit = IsvTool()
        file_list = IsvTool.decompress_gz_bz(args.package)
        induct_dict = boostkit.induct_log(*file_list)
        analysis_result = boostkit.analysis(**induct_dict)
        config_list = boostkit.acquire_os_msg(induct_dict.get("os_msg"))
        convert_result, effect_num, enable_list = boostkit.convert_analysis_res(**analysis_result)
        config_list += enable_list
        boostkit.generator_html("./new_report.html", effect_num, config_list, convert_result)
    else:
        print("请输入正确的参数，如-tool Devkit 或 -tool BoostKit")
    # except Exception as err:
    #     print(err)
    #     print("请输入正确的参数")



